package com.meterhuang.thread.zeroevenodd;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

/**
 * 1116. 打印零与奇偶数,可重入锁
 * http://leetcode-cn.com/problems/print-zero-even-odd/
 *
 * @author meterhuang
 * @date 2021/09/09 14:27
 */
public class ZeroEvenOddReentrant {

	private int n;

	public ZeroEvenOddReentrant(int n) {
		this.n = n;
	}

	private Lock lock = new ReentrantLock();

	private Condition zero = lock.newCondition();
	private Condition odd = lock.newCondition();
	private Condition even = lock.newCondition();
	private volatile int state = 0;

	public void zero(IntConsumer printNumber) {
		lock.lock();
		try {
			for (int i = 1; i <= n; ++i) {
				if (state != 0) {
					zero.await();
				}
				printNumber.accept(0);
				if (i % 2 == 1) {
					state = 1;
					odd.signal();
				} else {
					state = 2;
					even.signal();
				}
			}
		} catch (Exception e) {
		} finally {
			lock.unlock();
		}
	}

	public void even(IntConsumer printNumber) {
		lock.lock();
		try {
			for (int i = 2; i <= n; i = i + 2) {
				if (state != 2) {
					even.await();
				}
				if (i % 2 == 0) {
					state = 1;
					printNumber.accept(i);
					zero.signal();
				}
			}
		} catch (Exception e) {
		} finally {
			lock.unlock();
		}
	}

	public void odd(IntConsumer printNumber) {
		lock.lock();
		try {
			for (int i = 1; i <= n; i = i + 2) {
				if (state != 1) {
					odd.await();
				}
				if (i % 2 == 1) {
					state = 2;
					printNumber.accept(i);
					zero.signal();
				}
			}
		} catch (Exception e) {
		} finally {
			lock.unlock();
		}
	}
}
